home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / editor / j414src.arc / EXTEND.C < prev    next >
C/C++ Source or Header  |  1989-10-10  |  14KB  |  704 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is    *
  5.  * included in all the files.                                              *
  6.  ***************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "fp.h"
  10. #include "termcap.h"
  11. #include "ctype.h"
  12. #include "chars.h"
  13. #include "disp.h"
  14. #include "re.h"
  15.  
  16. #if defined(JOB_CONTROL) || defined(IPROCS)
  17. # include <signal.h>
  18. #endif
  19.  
  20. #ifdef MAC
  21. # include "mac.h"
  22. #else
  23. # ifdef    STDARGS
  24. #  include <stdarg.h>
  25. # else
  26. #  include <varargs.h>
  27. # endif
  28. #endif
  29.  
  30. #ifdef MSDOS
  31. # include <process.h>
  32. #endif
  33.  
  34. private void
  35.     DefAutoExec proto((struct data_obj *(*proc)()));
  36.  
  37. private int
  38.     match proto((char **, char *));
  39.  
  40. int    InJoverc = 0;
  41.  
  42. /* Auto execute code */
  43.  
  44. #define NEXECS    20
  45.  
  46. private struct {
  47.     char    *a_pattern;
  48.     data_obj    *a_cmd;
  49. } AutoExecs[NEXECS];    /* must be initialized by system to 0 */
  50.  
  51. private int    ExecIndex = 0;
  52.  
  53. /* Command auto-execute. */
  54.  
  55. void
  56. CAutoExec()
  57. {
  58.     DefAutoExec(findcom);
  59. }
  60.  
  61. /* Macro auto-execute. */
  62.  
  63. void
  64. MAutoExec()
  65. {
  66.     DefAutoExec(findmac);
  67. }
  68.  
  69. private void
  70. DefAutoExec(proc)
  71. #if defined(MAC) || defined(IBMPC)
  72. data_obj    *(*proc)();
  73. #else
  74. data_obj    *(*proc) proto((char *));
  75. #endif
  76. {
  77.     data_obj    *d;
  78.     char    *pattern;
  79.     int    i;
  80.  
  81.     if (ExecIndex >= NEXECS)
  82.         complain("Too many auto-executes, max %d.", NEXECS);
  83.     if ((d = (*proc)(ProcFmt)) == 0)
  84.         return;
  85.     pattern = do_ask("\r\n", (int (*) proto((int))) 0, (char *) 0, ": %f %s ",
  86.         d->Name);
  87.     for (i = 0; i < ExecIndex; i++) {
  88.         if (AutoExecs[i].a_cmd == d) {
  89.         char    *ipat = AutoExecs[i].a_pattern;
  90.  
  91.         if ((pattern == NULL || ipat == NULL)?
  92.             (pattern == ipat) : (strcmp(pattern, ipat) == 0))
  93.             return;        /* eliminate duplicates */
  94.         }
  95.     }
  96.     AutoExecs[ExecIndex].a_pattern = copystr(pattern);
  97.     AutoExecs[ExecIndex].a_cmd = d;
  98.     ExecIndex += 1;
  99. }
  100.  
  101. /* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the
  102.    same kind of file (i.e., match the same pattern) or OLD is 0 and it
  103.    matches, OR if the pattern is 0 (none was specified) then, we execute
  104.    the command associated with that kind of file. */
  105.  
  106. void
  107. DoAutoExec(new, old)
  108. register char    *new,
  109.         *old;
  110. {
  111.     register int    i;
  112.  
  113.     set_arg_value(1);
  114.     for (i = 0; i < ExecIndex; i++)
  115.         if ((AutoExecs[i].a_pattern == 0) ||
  116.             ((new != 0 && LookingAt(AutoExecs[i].a_pattern, new, 0)) &&
  117.              (old == 0 || !LookingAt(AutoExecs[i].a_pattern, old, 0))))
  118.             ExecCmd(AutoExecs[i].a_cmd);
  119. }
  120.  
  121. int
  122. addgetc()
  123. {
  124.     int    c;
  125.  
  126.     if (!InJoverc) {
  127.         Asking = strlen(mesgbuf);
  128.         c = getch();
  129.         Asking = 0;
  130.         add_mess("%p ", c);
  131.     } else {
  132.         c = getch();
  133.         if (c == '\n')
  134.             return EOF;    /* this isn't part of the sequence */
  135.         else if (c == '\\') {
  136.             if ((c = getch()) == LF)
  137.                 complain("[Premature end of line]");
  138.         } else if (c == '^') {
  139.             if ((c = getch()) == '?')
  140.                 c = RUBOUT;
  141.             else if (isalpha(c) || strchr("@[\\]^_", c))
  142.                 c = CTL(c);
  143.             else
  144.                 complain("[Unknown control character]");
  145.         }
  146.     }
  147.     return c;
  148. }
  149.  
  150. void
  151. Extend()
  152. {
  153.     data_obj    *d;
  154.  
  155.     if ((d = findcom(": ")) != NULL)
  156.         ExecCmd(d);
  157. }
  158.  
  159. /* Read a positive integer from CP.  It must be in base BASE, and
  160.    complains if it isn't.  If allints is nonzero, all the characters
  161.    in the string must be integers or we return -1; otherwise we stop
  162.    reading at the first nondigit. */
  163.  
  164. int
  165. chr_to_int(cp, base, allints, result)
  166. register char    *cp;
  167. int    base,
  168.     allints;
  169. register int    *result;
  170. {
  171.     register int    c;
  172.     int    value = 0,
  173.         sign;
  174.  
  175.     if ((c = *cp) == '-') {
  176.         sign = -1;
  177.         cp += 1;
  178.     } else
  179.         sign = 1;
  180.     while ((c = *cp++) != '\0') {
  181.         if (!isdigit(c)) {
  182.             if (allints == YES)
  183.                 return INT_BAD;
  184.             break;
  185.         }
  186.         c = c - '0';
  187.         if (c >= base)
  188.             complain("You must specify in base %d.", base);
  189.         value = value * base + c;
  190.     }
  191.     *result = value * sign;
  192.     return INT_OKAY;
  193. }
  194.  
  195. int
  196. ask_int(prompt, base)
  197. char    *prompt;
  198. int    base;
  199. {
  200.     char    *val = ask((char *) 0, prompt);
  201.     int    value;
  202.  
  203.     if (chr_to_int(val, base, YES, &value) == INT_BAD)
  204.         complain("That's not a number!");
  205.     return value;
  206. }
  207.  
  208. void
  209. vpr_aux(vp, buf)
  210. register const struct variable    *vp;
  211. char    *buf;
  212. {
  213.     switch (vp->v_flags & V_TYPEMASK) {
  214.     case V_BASE10:
  215.         swritef(buf, "%d", *((int *) vp->v_value));
  216.         break;
  217.  
  218.     case V_BASE8:
  219.         swritef(buf, "%o", *((int *) vp->v_value));
  220.         break;
  221.  
  222.     case V_BOOL:
  223.         swritef(buf, (*((int *) vp->v_value)) ? "on" : "off");
  224.         break;
  225.  
  226.     case V_STRING:
  227.     case V_FILENAME:
  228.         swritef(buf, "%s", vp->v_value);
  229.         break;
  230.  
  231.     case V_CHAR:
  232.         swritef(buf, "%p", *((int *) vp->v_value));
  233.         break;
  234.     }
  235. }
  236.  
  237. void
  238. PrVar()
  239. {
  240.     struct variable    *vp;
  241.     char    prbuf[256];
  242.  
  243.     if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
  244.         return;
  245.     vpr_aux(vp, prbuf);
  246.     s_mess(": %f %s => %s", vp->Name, prbuf);
  247. }
  248.  
  249. void
  250. SetVar()
  251. {
  252.     struct variable    *vp;
  253.     char    *prompt;
  254.  
  255.     if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
  256.         return;
  257.     prompt = sprint(": %f %s ", vp->Name);
  258.  
  259.     switch (vp->v_flags & V_TYPEMASK) {
  260.     case V_BASE10:
  261.     case V_BASE8:
  262.         {
  263.         int    value;
  264.  
  265.         value = ask_int(prompt, ((vp->v_flags & V_TYPEMASK) == V_BASE10)
  266.                       ? 10 : 8);
  267.         *((int *) vp->v_value) = value;
  268.         break;
  269.         }
  270.  
  271.     case V_BOOL:
  272.         {
  273.         char    *def = *((int *) vp->v_value) ? "off" : "on",
  274.             *on_off;
  275.         int    value;
  276.  
  277.         on_off = ask(def, prompt);
  278.         if (casecmp(on_off, "on") == 0)
  279.             value = ON;
  280.         else if (casecmp(on_off, "off") == 0)
  281.             value = OFF;
  282.         else
  283.             complain("Boolean variables must be ON or OFF.");
  284.         *((int *) vp->v_value) = value;
  285. #ifdef MAC
  286.         MarkVar(vp,-1,0);    /* mark the menu item */
  287. #endif
  288.         s_mess("%s%s", prompt, value ? "on" : "off");
  289.         break;
  290.         }
  291.  
  292.     case V_FILENAME:
  293.         {
  294.         char    fbuf[FILESIZE];
  295.  
  296.         swritef(&prompt[strlen(prompt)], "(default %s) ", vp->v_value);
  297.         (void) ask_file(prompt, (char *) vp->v_value, fbuf);
  298.         strcpy((char *) vp->v_value, fbuf);
  299.         break;
  300.         }
  301.  
  302.     case V_STRING:
  303.         {
  304.         char    *str;
  305.  
  306.         /* Do_ask() so you can set string to "" if you so desire. */
  307.         str = do_ask("\r\n", (int (*) proto((int))) 0, (char *) vp->v_value,
  308.             prompt);
  309.         if (str == 0)
  310.             str = NullStr;
  311.         strcpy(vp->v_value, str);
  312.         /* ... and hope there is enough room. */
  313.         break;
  314.         }
  315.     case V_CHAR:
  316.         f_mess(prompt);
  317.         *((int *) vp->v_value) = addgetc();
  318.         break;
  319.  
  320.     }
  321.     if (vp->v_flags & V_MODELINE)
  322.         UpdModLine = YES;
  323.     if (vp->v_flags & V_CLRSCREEN) {
  324. #ifdef IBMPC
  325.         setcolor(Fgcolor, Bgcolor);
  326. #endif /* IBMPC */
  327.         ClAndRedraw();
  328.     }
  329.     if (vp->v_flags & V_TTY_RESET)
  330.         tty_reset();
  331. }
  332.  
  333. /* Command completion - possible is an array of strings, prompt is
  334.    the prompt to use, and flags are ... well read jove.h.
  335.  
  336.    If flags are RET_STATE, and the user hits <return> what they typed
  337.    so far is in the Minibuf string. */
  338.  
  339. private char    **Possible;
  340. private int    comp_value,
  341.         comp_flags;
  342.  
  343. int
  344. aux_complete(c)
  345. int    c;
  346. {
  347.     int    command,
  348.         i;
  349.  
  350.     if (comp_flags & CASEIND) {
  351.         char    *lp;
  352.  
  353.         for (lp = linebuf; *lp != '\0'; lp++)
  354. #if (defined(IBMPC) || defined(MAC))
  355.             lower(lp);
  356. #else
  357.             if (isupper(*lp))
  358.                 *lp = tolower(*lp);
  359. #endif
  360.     }
  361.     switch (c) {
  362.     case EOF:
  363.         comp_value = -1;
  364.         return 0;
  365.  
  366.     case '\r':
  367.     case '\n':
  368.         command = match(Possible, linebuf);
  369.         if (command >= 0) {
  370.             comp_value = command;
  371.             return 0;    /* tells ask to stop */
  372.         }
  373.         if (eolp() && bolp()) {
  374.             comp_value = NULLSTRING;
  375.             return 0;
  376.         }
  377.         if (comp_flags & RET_STATE) {
  378.             comp_value = command;
  379.             return 0;
  380.         }
  381.         if (InJoverc)
  382.             complain("[\"%s\" unknown]", linebuf);
  383.         rbell();
  384.         break;
  385.  
  386.     case '\t':
  387.     case ' ':
  388.         {
  389.         int    minmatch = 1000,
  390.             maxmatch = 0,
  391.             numfound = 0,
  392.             lastmatch = -1,
  393.             len = strlen(linebuf);
  394.  
  395.         for (i = 0; Possible[i] != 0; i++) {
  396.             int    this_len;
  397.  
  398.             this_len = numcomp(Possible[i], linebuf);
  399.             maxmatch = max(maxmatch, this_len);
  400.             if (this_len >= len) {
  401.                 if (numfound)
  402.                     minmatch = min(minmatch, numcomp(Possible[lastmatch], Possible[i]));
  403.                 else
  404.                     minmatch = strlen(Possible[i]);
  405.                 numfound += 1;
  406.                 lastmatch = i;
  407.                 if (strcmp(linebuf, Possible[i]) == 0)
  408.                     break;
  409.             }
  410.         }
  411.  
  412.         if (numfound == 0) {
  413.             rbell();
  414.             if (InJoverc)
  415.                 complain("[\"%s\" unknown]", linebuf);
  416.             /* If we're not in the .joverc then
  417.                let's do something helpful for the
  418.                user. */
  419.             if (maxmatch < len) {
  420.                 char    *cp;
  421.  
  422.                 cp = linebuf + maxmatch;
  423.                 *cp = 0;
  424.                 Eol();
  425.             }
  426.             break;
  427.         }
  428.         if (c != '\t' && numfound == 1) {
  429.             comp_value = lastmatch;
  430.             return 0;
  431.         }
  432.         null_ncpy(linebuf, Possible[lastmatch], (size_t) minmatch);
  433.         Eol();
  434.         if (minmatch == len)    /* No difference */
  435.             rbell();
  436.         break;
  437.         }
  438.  
  439.     case '?':
  440.         {
  441.         int    len;
  442.  
  443.         if (InJoverc)
  444.             complain((char *) 0);
  445.         /* kludge: in case we're using UseBuffers, in which case
  446.            linebuf gets written all over */
  447.         strcpy(Minibuf, linebuf);
  448.         len = strlen(Minibuf);
  449.         TOstart("Completion", TRUE);    /* for now ... */
  450.         for (i = 0; Possible[i]; i++)
  451.             if (numcomp(Possible[i], Minibuf) >= len) {
  452.                 Typeout(Possible[i]);
  453.                 if (TOabort != 0)
  454.                     break;
  455.             }
  456.  
  457.         TOstop();
  458.         }
  459.         break;
  460.     }
  461.     return !FALSE;
  462. }
  463.  
  464. int
  465. complete(possible, prompt, flags)
  466. register char    *possible[];
  467. char    *prompt;
  468. int    flags;
  469. {
  470.     /* protect static "Possible" from being overwritten due to recursion */
  471.     if (InRealAsk)
  472.         complain((char *) NULL);
  473.  
  474.     Possible = possible;
  475.     comp_flags = flags;
  476.     (void) do_ask("\r\n \t?", aux_complete, NullStr, prompt);
  477.     return comp_value;
  478. }
  479.  
  480. private int
  481. match(choices, what)
  482. register char    **choices,
  483.         *what;
  484. {
  485.     register size_t    len;
  486.     int    i,
  487.         found = 0,
  488.         save,
  489.         exactmatch = -1;
  490.  
  491.     len = strlen(what);
  492.     if (len == 0)
  493.         return NULLSTRING;
  494.     for (i = 0; choices[i]; i++) {
  495.         if (strncmp(what, choices[i], len) == 0) {
  496.             if (strcmp(what, choices[i]) == 0)
  497.                 exactmatch = i;
  498.             save = i;
  499.             found += 1;    /* found one */
  500.         }
  501.     }
  502.  
  503.     if (found == 0)
  504.         save = ORIGINAL;
  505.     else if (found > 1) {
  506.         if (exactmatch != -1)
  507.             save = exactmatch;
  508.         else
  509.             save = AMBIGUOUS;
  510.     }
  511.  
  512.     return save;
  513. }
  514.  
  515. void
  516. Source()
  517. {
  518.     extern char    *getenv();
  519.     char    *com,
  520.         buf[FILESIZE];
  521.  
  522. #ifndef MSDOS
  523.     swritef(buf, "%s/.joverc", HomeDir);
  524. #else /* MSDOS */
  525.     if (com = getenv("JOVERC"))
  526.         strcpy(buf, com);
  527.     else
  528.         strcpy(buf, Joverc);
  529. #endif /* MSDOS */
  530.     com = ask_file((char *) 0, buf, buf);
  531.     if (joverc(buf) == 0)
  532.         complain(IOerr("read", com));
  533. }
  534.  
  535. void
  536. BufPos()
  537. {
  538.     register Line    *lp = curbuf->b_first;
  539.     register int    i,
  540.             dotline;
  541.     long    dotchar,
  542.         nchars;
  543.  
  544.     for (i = nchars = 0; lp != 0; i++, lp = lp->l_next) {
  545.         if (lp == curline) {
  546.             dotchar = nchars + curchar;
  547.             dotline = i + 1;
  548.         }
  549.         nchars += length(lp) + (lp->l_next != 0); /* include the NL */
  550.     }
  551.  
  552.     s_mess("[\"%s\" line %d/%d, char %D/%D (%d%%), cursor = %d/%d]",
  553.            filename(curbuf), dotline, i, dotchar, nchars,
  554.            (nchars == 0) ? 100 : (int) (((long) dotchar * 100) / nchars),
  555.            calc_pos(linebuf, curchar),
  556.            calc_pos(linebuf, (int)strlen(linebuf)));
  557. }
  558.  
  559. #define IF_UNBOUND    (-1)
  560. #define IF_TRUE        1
  561. #define IF_FALSE    (!IF_TRUE)
  562.  
  563. #ifndef MAC
  564. private int
  565. do_if(cmd)
  566. char    *cmd;
  567. {
  568. #ifdef MSDOS
  569.     int status;
  570. #else
  571.     int    pid,
  572.         status;
  573. #endif /* MSDOS */
  574. #ifndef MSDOS
  575.  
  576.     switch (pid = fork()) {
  577.     case -1:
  578.         complain("[Fork failed: if]");
  579.         /*NOTREACHED*/
  580.  
  581.     case 0:
  582.         {
  583. #endif /* MSDOS */
  584.         char    *args[12],
  585.             *cp = cmd,
  586.             **ap = args;
  587.  
  588.         *ap++ = cmd;
  589.         for (;;) {
  590.             if ((cp = strchr(cp, ' ')) == 0)
  591.                 break;
  592.             *cp++ = '\0';
  593.             *ap++ = cp;
  594.         }
  595.         *ap = 0;
  596.  
  597. #ifndef MSDOS
  598.         close(0);    /*    we want reads to fail */
  599.         /* close(1);     but not writes or ioctl's
  600.         close(2);    */
  601. #else /* MSDOS */
  602.     if ((status = spawnvp(0, args[0], args)) < 0)
  603.         complain("[Spawn failed: if]");
  604. #endif /* MSDOS */
  605.  
  606. #ifndef MSDOS
  607.         (void) execvp(args[0], (const char **)args);
  608.         _exit(-10);    /* signals exec error (see below) */
  609.         }
  610.     }
  611. #ifdef IPROCS
  612.     SigHold(SIGCHLD);
  613. #endif
  614.     dowait(pid, &status);
  615. #ifdef IPROCS
  616.     SigRelse(SIGCHLD);
  617. #endif
  618.     if (status == -10)
  619.         complain("[Exec failed]");
  620.     if (status < 0)
  621.         complain("[Exit %d]", status);
  622. #endif /* MSDOS */
  623.     return (status == 0);    /* 0 means successful */
  624. }
  625. #endif /* MAC */
  626.  
  627. int
  628. joverc(file)
  629. char    *file;
  630. {
  631.     char    buf[LBSIZE],
  632.         lbuf[LBSIZE];
  633.     int    lnum = 0,
  634.         eof = FALSE;
  635.     jmp_buf    savejmp;
  636.     int    IfStatus = IF_UNBOUND;
  637.     File    *fp;
  638.  
  639.     fp = open_file(file, buf, F_READ, NO, YES);
  640.     if (fp == NIL)
  641.         return NO;    /* joverc returns an integer */
  642.  
  643.     /* Catch any errors, here, and do the right thing with them,
  644.        and then restore the error handle to whoever did a setjmp
  645.        last. */
  646.  
  647.     InJoverc += 1;
  648.     push_env(savejmp);
  649.     if (setjmp(mainjmp)) {
  650.         Buffer    *savebuf = curbuf;
  651.  
  652.         SetBuf(do_select((Window *) 0, "RC errors"));
  653.         ins_str(sprint("%s:%d:%s\t%s\n", pr_name(file, YES), lnum, lbuf, mesgbuf), NO);
  654.         unmodify();
  655.         SetBuf(savebuf);
  656.         Asking = 0;
  657.     }
  658.     if (!eof) do {
  659.         eof = (f_gets(fp, lbuf, sizeof lbuf) == EOF);
  660.         lnum += 1;
  661.         if (lbuf[0] == '#')        /* a comment */
  662.             continue;
  663. #ifndef MAC
  664.         if (casencmp(lbuf, "if", (size_t)2) == 0) {
  665.             char    cmd[128];
  666.  
  667.             if (IfStatus != IF_UNBOUND)
  668.                 complain("[Cannot have nested if's]");
  669.             if (LookingAt("if[ \t]*\\(.*\\)$", lbuf, 0) == 0)
  670.                 complain("[If syntax error]");
  671.             putmatch(1, cmd, sizeof cmd);
  672.             IfStatus = do_if(cmd) ? IF_TRUE : IF_FALSE;
  673.             continue;
  674.         } else if (casencmp(lbuf, "else", (size_t)4) == 0) {
  675.             if (IfStatus == IF_UNBOUND)
  676.                 complain("[Unexpected `else']");
  677.             IfStatus = !IfStatus;
  678.             continue;
  679.         } else if (casencmp(lbuf, "endif", (size_t)5) == 0) {
  680.             if (IfStatus == IF_UNBOUND)
  681.                 complain("[Unexpected `endif']");
  682.             IfStatus = IF_UNBOUND;
  683.             continue;
  684.         }
  685. #endif
  686.         if (IfStatus == IF_FALSE)
  687.             continue;
  688.         (void) strcat(lbuf, "\n");
  689.         Inputp = lbuf;
  690.         while (*Inputp == ' ' || *Inputp == '\t')
  691.             Inputp += 1;    /* skip white space */
  692.         Extend();
  693.     } while (!eof);
  694.  
  695.     f_close(fp);
  696.     pop_env(savejmp);
  697.     Inputp = 0;
  698.     Asking = 0;
  699.     InJoverc -= 1;
  700.     if (IfStatus != IF_UNBOUND)
  701.         complain("[Missing endif]");
  702.     return 1;
  703. }
  704.